#ifndef ISMARTPTR_H_
#define ISMARTPTR_H_



template <class T> class ISmartPtr;


/////////////////////////////////////////
// IRefCount
// is an interface for reference counting
// Classes can implement it themselves,
// or SmartPtr will provide its internal implementation of IRefCount
template <class T>
class IRefCount
{
	friend class ISmartPtr<T>;
protected:
	virtual void __IncRefCount() = 0;
	virtual void __DecRefCount() = 0;
	virtual T* GetPtr() const = 0;
};


template <class T>
class IRefCountImpl : public IRefCount<T>
{
private:
	int __m_counter;

protected:
	virtual void __IncRefCount()
	{
		__m_counter++;
	}
	virtual void __DecRefCount()
	{
		__m_counter--;
		if(__m_counter <= 0)
		{
			__DestroyRef();
		}
	}
	virtual T* GetPtr() const
	{
		return ((T*)this);
	}
	virtual void __DestroyRef()
	{
		if(GetPtr() != 0)
			delete GetPtr();
	}
protected:
	IRefCountImpl()
	{
		__m_counter = 0;
	}
};

template <class T>
class ISmartPtr
{
private:
	IRefCount<T>* __m_refcount;

	/////////////////////////////////////////
	// __RefCounter
	// An internal implementation of IRefCount
	// for classes that don't implement it
	// SmartPtr will automatically choose between its internal and
	// class' implementation of IRefCount
	class __RefCounter : public IRefCountImpl<T>
	{
	private:
		T* __m_ptr;

	protected:
		virtual T* GetPtr() const
		{
			return __m_ptr;
		}
		virtual void __DestroyRef()
		{
			delete this;
		}

	public:
		__RefCounter(T* ptr)
		{
			__m_ptr = ptr;
		}
		virtual ~__RefCounter()
		{
			IRefCountImpl<T>::__DestroyRef();
		}
	};

	// this method is called if T does not implement IRefCount
	void __Assign(void* ptr)
	{
		if(ptr == 0)
			__Assign((IRefCount<T>*)0);
		else
		{
			__Assign(new __RefCounter(static_cast<T*>(ptr)));
		}
	}

	// this method is picked over __Assign(void *ptr)
	// if T implements IRefCount.
	// This allows some memory usage optimization
	void __Assign(IRefCount<T>* refcount)
	{
		if(refcount != 0) refcount->__IncRefCount();
		IRefCount<T>* oldref = __m_refcount;
		__m_refcount = refcount;
		if(oldref != 0) oldref->__DecRefCount();
	}

public:
	ISmartPtr()
	{
		__m_refcount = 0;
	}
	ISmartPtr(T* ptr)
	{
		__m_refcount = 0;
		__Assign(ptr);
	}
	ISmartPtr(const ISmartPtr& sp)
	{
		__m_refcount = 0;
		__Assign(sp.__m_refcount);
	}
	virtual ~ISmartPtr()
	{
		__Assign((IRefCount<T>*)0);
	}

	// get the contained pointer, not really needed but...
	T* GetPtr() const
	{
		if(__m_refcount == 0)
			return 0;

		return __m_refcount->GetPtr();
	}

	// assign another smart pointer
	ISmartPtr& operator = (const ISmartPtr& sp)
	{
		__Assign(sp.__m_refcount);
		return *this;
	}
	// assign pointer or NULL
	ISmartPtr& operator = (T* ptr)
	{
		__Assign(ptr);
		return *this;
	}

	// to access members of T
	T* operator -> ()
	{
#ifdef _ASSERT
		_ASSERT(GetPtr() != 0);
#endif
		return GetPtr();
	}

	// conversion to T* (for function calls)
	operator T* () const
	{
		return GetPtr();
	}

	// utilities
	bool operator ! ()
	{
		return GetPtr() == 0;
	}
	bool operator == (const ISmartPtr& sp)
	{
		return GetPtr() == sp.GetPtr();
	}
	bool operator != (const ISmartPtr& sp)
	{
		return GetPtr() != sp.GetPtr();
	}

};

#endif /*ISMARTPTR_H_*/
